package com.ainge.easystudy.thread;

import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: Ainge
 * @Time: 2021/6/12 13:21
 */
public class ConditionAndLockDemo {


    public static void main(String[] args) {

        LockResource resource = new LockResource();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                resource.printAAA();
            }
        },ThreadName.AAA.name()).start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                resource.printBBB();
            }
        },ThreadName.BBB.name()).start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                resource.printCCC();
            }
        },ThreadName.CCC.name()).start();

    }

}

class LockResource {
    private final Lock lock = new ReentrantLock();
    private final Condition[] conditionArr =  {lock.newCondition(),lock.newCondition(),lock.newCondition()};
    // 默认AAA线程开始
    private volatile ThreadName threadName = ThreadName.getFirst();

    public void printAAA(){
        try {
            doPrint(ThreadName.AAA);
        }catch (Exception e){
        }
    }
    public void printBBB(){
        try {
            doPrint(ThreadName.BBB);
        }catch (Exception e){
        }
    }
    public void printCCC(){
        try {
            doPrint(ThreadName.CCC);
        }catch (Exception e){
        }
    }
    private void doPrint(ThreadName threadName) throws Exception{
        try{
            lock.lock();
            while (this.threadName != threadName){
                conditionArr[threadName.getCurrent()].await();
            }
            System.out.println(Thread.currentThread().getName()+"\t "+threadName.name());
            this.threadName = ThreadName.next(threadName);
            conditionArr[threadName.getNext()].signal();
        }finally {
            lock.unlock();
        }
    }
}

enum ThreadName{
    AAA(0,1),
    BBB(1,2),
    CCC(2,0);

    private final int current;
    private final int next;
    ThreadName(int current,int next){
        this.current = current;
        this.next = next;
    }
    public static ThreadName next(ThreadName current){
        Integer next = Optional.ofNullable(current).map(c -> c.next).orElse(0);
        return Arrays.stream(values()).filter(v -> v.current == next).findAny().orElse(null);
    }

    public int getCurrent() {
        return current;
    }

    public int getNext() {
        return next;
    }
    public static ThreadName getFirst(){
        return ThreadName.AAA;
    }
}




